/******************************************************************************
 * QwtPolar Widget Library
 * Copyright (C) 2008   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_polar_canvas.h"
#include "qwt_polar_plot.h"
#include "qwt_painter.h"

#include <qpainter.h>
#include <qevent.h>
#include <qpixmap.h>
#include <qstyle.h>
#include <qstyleoption.h>
#ifdef Q_WS_X11
#include <qx11info_x11.h>
#endif

class QwtPolarCanvas::PrivateData
{
public:
    PrivateData() : backingStore(NULL)
    {
    }

    ~PrivateData()
    {
        delete backingStore;
    }

    QwtPolarCanvas::PaintAttributes paintAttributes;
    QPixmap* backingStore;
};

//! Constructor
QwtPolarCanvas::QwtPolarCanvas(QwtPolarPlot* plot) : QFrame(plot)
{
    m_data = new PrivateData;

#ifndef QT_NO_CURSOR
    setCursor(Qt::CrossCursor);
#endif
    setFocusPolicy(Qt::WheelFocus);

    setPaintAttribute(BackingStore, true);
}

//! Destructor
QwtPolarCanvas::~QwtPolarCanvas()
{
    delete m_data;
}

//! \return Parent plot widget
QwtPolarPlot* QwtPolarCanvas::plot()
{
    return qobject_cast< QwtPolarPlot* >(parent());
}

//! \return Parent plot widget
const QwtPolarPlot* QwtPolarCanvas::plot() const
{
    return qobject_cast< QwtPolarPlot* >(parent());
}

/*!
   \brief Changing the paint attributes

   \param attribute Paint attribute
   \param on On/Off

   The default setting enables BackingStore

   \sa testPaintAttribute(), paintCache()
 */
void QwtPolarCanvas::setPaintAttribute(PaintAttribute attribute, bool on)
{
    if (bool(m_data->paintAttributes & attribute) == on)
        return;

    if (on)
        m_data->paintAttributes |= attribute;
    else
        m_data->paintAttributes &= ~attribute;

    switch (attribute) {
    case BackingStore: {
        if (on) {
            if (m_data->backingStore == NULL)
                m_data->backingStore = new QPixmap();

            if (isVisible()) {
                const QRect cr = contentsRect();
#if QT_VERSION >= 0x050000
                *m_data->backingStore = grab(cr);
#else
                *m_data->backingStore = QPixmap::grabWidget(this, cr);
#endif
            }
        } else {
            delete m_data->backingStore;
            m_data->backingStore = NULL;
        }
        break;
    }
    }
}

/*!
   Test whether a paint attribute is enabled

   \param attribute Paint attribute
   \return true if the attribute is enabled
   \sa setPaintAttribute()
 */
bool QwtPolarCanvas::testPaintAttribute(PaintAttribute attribute) const
{
    return (m_data->paintAttributes & attribute) != 0;
}

//! \return Backing store, might be null
const QPixmap* QwtPolarCanvas::backingStore() const
{
    return m_data->backingStore;
}

//! Invalidate the internal backing store
void QwtPolarCanvas::invalidateBackingStore()
{
    if (m_data->backingStore)
        *m_data->backingStore = QPixmap();
}

/*!
   Paint event
   \param event Paint event
 */
void QwtPolarCanvas::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    painter.setClipRegion(event->region());

    if ((m_data->paintAttributes & BackingStore) && m_data->backingStore != NULL) {
        QPixmap& bs = *m_data->backingStore;
        if (bs.size() != size()) {
            bs = QPixmap(size());
#ifdef Q_WS_X11
            if (bs.x11Info().screen() != x11Info().screen())
                bs.x11SetScreen(x11Info().screen());
#endif

            QPainter p;

            if (testAttribute(Qt::WA_StyledBackground)) {
                p.begin(&bs);
                QwtPainter::drawStyledBackground(this, &p);
            } else {
                if (autoFillBackground()) {
                    p.begin(&bs);
                    p.fillRect(rect(), palette().brush(backgroundRole()));
                } else {
                    QWidget* bgWidget = QwtPainter::findBackgroundWidget(plot());

                    QwtPainter::fillPixmap(bgWidget, bs, mapTo(bgWidget, rect().topLeft()));

                    p.begin(&bs);
                }
            }

            plot()->drawCanvas(&p, contentsRect());

            if (frameWidth() > 0)
                drawFrame(&p);
        }

        painter.drawPixmap(0, 0, *m_data->backingStore);
    } else {
        QwtPainter::drawStyledBackground(this, &painter);

        plot()->drawCanvas(&painter, contentsRect());

        if (frameWidth() > 0)
            drawFrame(&painter);
    }
}

/*!
   Resize event
   \param event Resize event
 */
void QwtPolarCanvas::resizeEvent(QResizeEvent* event)
{
    QFrame::resizeEvent(event);

    for (int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++)
        plot()->updateScale(scaleId);
}

/*!
    Translate a point from widget into plot coordinates

    \param pos Point in widget coordinates of the plot canvas
    \return Point in plot coordinates

    \sa transform()
 */
QwtPointPolar QwtPolarCanvas::invTransform(const QPoint& pos) const
{
    const QwtPolarPlot* pl = plot();

    const QwtScaleMap azimuthMap = pl->scaleMap(QwtPolar::Azimuth);
    const QwtScaleMap radialMap  = pl->scaleMap(QwtPolar::Radius);

    const QPointF center = pl->plotRect().center();

    double dx = pos.x() - center.x();
    double dy = -(pos.y() - center.y());

    const QwtPointPolar polarPos = QwtPointPolar(QPoint(dx, dy)).normalized();

    double azimuth = azimuthMap.invTransform(polarPos.azimuth());

    // normalize the azimuth
    double min = azimuthMap.s1();
    double max = azimuthMap.s2();
    if (max < min)
        qSwap(min, max);

    if (azimuth < min) {
        azimuth += max - min;
    } else if (azimuth > max) {
        azimuth -= max - min;
    }

    const double radius = radialMap.invTransform(polarPos.radius());

    return QwtPointPolar(azimuth, radius);
}

/*!
    Translate a point from plot into widget coordinates

    \param polarPos Point in plot coordinates
    \return Point in widget coordinates
    \sa transform()
 */
QPoint QwtPolarCanvas::transform(const QwtPointPolar& polarPos) const
{
    const QwtPolarPlot* pl = plot();

    const QwtScaleMap azimuthMap = pl->scaleMap(QwtPolar::Azimuth);
    const QwtScaleMap radialMap  = pl->scaleMap(QwtPolar::Radius);

    const double radius  = radialMap.transform(polarPos.radius());
    const double azimuth = azimuthMap.transform(polarPos.azimuth());

    const QPointF pos = qwtPolar2Pos(pl->plotRect().center(), radius, azimuth);

    return pos.toPoint();
}
